package lyl.file;

import java.io.*;
import java.net.*;
import java.util.*;


/**
 * 此类中封装一些常用的文件操作。 所有方法都是静态方法，不需要生成此类的实例， 为避免生成此类的实例，构造方法被申明为private类型的。
 * 
 * @since 0.1
 */

public final class FileUtil {
    /**
     * 私有构造方法，防止类的实例化，因为工具类不需要实例化。
     */
    private FileUtil() {

    }

    /**
     * 修改文件的最后访问时间。 如果文件不存在则创建该文件。 &ltb>目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考虑中。</b>
     * 
     * @param file
     *            需要修改最后访问时间的文件。
     * @since 0.1
     */
    public static void touch(File file) {
        long currentTime = System.currentTimeMillis();
        if (!file.exists()) {
            System.err.println("file not found:" + file.getName());
            System.err.println("Create a new file:" + file.getName());
            try {
                if (file.createNewFile()) {
                    System.out.println("Succeeded!");
                }
                else {
                    System.err.println("Create file failed!");
                }
            }
            catch (IOException e) {
                System.err.println("Create file failed!");
                e.printStackTrace();
            }
        }
        boolean result = file.setLastModified(currentTime);
        if (!result) {
            System.err.println("touch failed: " + file.getName());
        }
    }

    /**
     * 修改文件的最后访问时间。 如果文件不存在则创建该文件。 &ltb>目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考虑中。</b>
     * 
     * @param fileName
     *            需要修改最后访问时间的文件的文件名。
     * @since 0.1
     */
    public static void touch(String fileName) {
        File file = new File(fileName);
        touch(file);
    }

    /**
     * 修改文件的最后访问时间。 如果文件不存在则创建该文件。 &ltb>目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考虑中。</b>
     * 
     * @param files
     *            需要修改最后访问时间的文件数组。
     * @since 0.1
     */
    public static void touch(File[] files) {
        for (int i = 0; i < files.length; i++) {
            touch(files[i]);
        }
    }

    /**
     * 修改文件的最后访问时间。 如果文件不存在则创建该文件。 &ltb>目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考虑中。</b>
     * 
     * @param fileNames
     *            需要修改最后访问时间的文件名数组。
     * @since 0.1
     */
    public static void touch(String[] fileNames) {
        File[] files = new File[fileNames.length];
        for (int i = 0; i < fileNames.length; i++) {
            files[i] = new File(fileNames[i]);
        }
        touch(files);
    }

    /**
     * 判断指定的文件是否存在。
     * 
     * @param fileName
     *            要判断的文件的文件名
     * @return 存在时返回true，否则返回false。
     * @since 0.1
     */
    public static boolean isFileExist(String fileName) {
        return new File(fileName).isFile();
    }

    /**
     * 创建指定的目录。 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。 &ltb>注意：可能会在返回false的时候创建部分父目录。</b>
     * 
     * @param file
     *            要创建的目录
     * @return 完全创建成功时返回true，否则返回false。
     * @since 0.1
     */
    public static boolean makeDirectory(File file) {
        File parent = file.getParentFile();
        if (parent != null) {
            return parent.mkdirs();
        }
        return false;
    }

    /**
     * 创建指定的目录。 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。 &ltb>注意：可能会在返回false的时候创建部分父目录。</b>
     * 
     * @param fileName
     *            要创建的目录的目录名
     * @return 完全创建成功时返回true，否则返回false。
     * @since 0.1
     */
    public static boolean makeDirectory(String fileName) {
        File file = new File(fileName);
        return makeDirectory(file);
    }

    /**
     * 清空指定目录中的文件。 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都会返回false。 另外这个方法不会迭代删除，即不会删除子目录及其内容。
     * 
     * @param directory
     *            要清空的目录
     * @return 目录下的所有文件都被成功删除时返回true，否则返回false.
     * @since 0.1
     */
    public static boolean emptyDirectory(File directory) {
        boolean result = false;
        File[] entries = directory.listFiles();
        for (int i = 0; i < entries.length; i++) {
            if (!entries[i].delete()) {
                result = false;
            }
        }
        return true;
    }

    /**
     * 清空指定目录中的文件。 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都会返回false。 另外这个方法不会迭代删除，即不会删除子目录及其内容。
     * 
     * @param directoryName
     *            要清空的目录的目录名
     * @return 目录下的所有文件都被成功删除时返回true，否则返回false。
     * @since 0.1
     */
    public static boolean emptyDirectory(String directoryName) {
        File dir = new File(directoryName);
        return emptyDirectory(dir);
    }

    /**
     * 删除指定目录及其中的所有内容。
     * 
     * @param dirName
     *            要删除的目录的目录名
     * @return 删除成功时返回true，否则返回false。
     * @since 0.1
     */
    public static boolean deleteDirectory(String dirName) {
        return deleteDirectory(new File(dirName));
    }

    /**
     * 删除指定目录及其中的所有内容。
     * 
     * @param dir
     *            要删除的目录
     * @return 删除成功时返回true，否则返回false。
     * @since 0.1
     */
    public static boolean deleteDirectory(File dir) {
        if ((dir == null) || !dir.isDirectory()) {
            // throw new IllegalArgumentException("Argument " + dir + " is not a directory. ");
            System.err.println("Argument " + dir + " is not a directory. ");
            return true;
        }

        File[] entries = dir.listFiles();
        int sz = entries.length;

        for (int i = 0; i < sz; i++) {
            if (entries[i].isDirectory()) {
                if (!deleteDirectory(entries[i])) {
                    return false;
                }
            }
            else {
                if (!entries[i].delete()) {
                    return false;
                }
            }
        }

        if (!dir.delete()) {
            return false;
        }
        return true;
    }

    /**
     * 删除文件
     * 
     * @param fileName
     * @return
     */
    public static boolean deleteFile(String fileName) {
        File file = new File(fileName);
        if (file.exists())
            return file.delete();

        return false;
    }

    /**
     * 列出目录中的所有内容，包括其子目录中的内容。
     * 
     * @param fileName
     *            要列出的目录的目录名
     * @return 目录内容的文件数组。
     * @since 0.1
     */
    public static File[] listAll(String fileName) {
        return listAll(new File(fileName));
    }

    /**
     * 列出目录中的所有内容，包括其子目录中的内容。
     * 
     * @param file
     *            要列出的目录
     * @return 目录内容的文件数组。
     * @since 0.1
     */
    public static File[] listAll(File file) {
        ArrayList list = new ArrayList();
        File[] files;
        if (!file.exists() || file.isFile()) {
            return new File[0];
        }
        list(list, file, new AllFileFilter());
        list.remove(file);
        files = new File[list.size()];
        list.toArray(files);
        return files;
    }

    /**
     * 列出目录中的所有内容，包括其子目录中的内容。
     * 
     * @param file
     *            要列出的目录
     * @param filter
     *            过滤器
     * @return 目录内容的文件数组。
     * @since 0.1
     */
    public static File[] listAll(File file, javax.swing.filechooser.FileFilter filter) {
        ArrayList list = new ArrayList();
        File[] files;
        if (!file.exists() || file.isFile()) {
            return new File[0];
        }
        list(list, file, filter);
        files = new File[list.size()];
        list.toArray(files);
        return files;
    }

    /**
     * 将目录中的内容添加到列表。
     * 
     * @param list
     *            文件列表
     * @param filter
     *            过滤器
     * @param file
     *            目录
     */
    private static void list(ArrayList list, File file, javax.swing.filechooser.FileFilter filter) {
        if (filter.accept(file)) {
            list.add(file);
            if (file.isFile()) {
                return;
            }
        }
        if (file.isDirectory()) {
            File files[] = file.listFiles();
            for (int i = 0; i < files.length; i++) {
                list(list, files[i], filter);
            }
        }

    }

    /**
     * 返回文件的URL地址。
     * 
     * @param file
     *            文件
     * @return 文件对应的的URL地址
     * @throws MalformedURLException
     * @since 0.4
     * @deprecated 在实现的时候没有注意到File类本身带一个toURL方法将文件路径转换为URL。 请使用File.toURL方法。
     */
    public static URL getURL(File file) throws MalformedURLException {
        String fileURL = "file:/" + file.getAbsolutePath();
        URL url = new URL(fileURL);
        return url;
    }

    /**
     * 从文件路径得到文件名。
     * 
     * @param filePath
     *            文件的路径，可以是相对路径也可以是绝对路径
     * @return 对应的文件名
     * @since 0.4
     */
    public static String getFileName(String filePath) {
        File file = new File(filePath);
        return file.getName();
    }

    /**
     * 从文件名得到文件绝对路径。
     * 
     * @param fileName
     *            文件名
     * @return 对应的文件路径
     * @since 0.4
     */
    public static String getFilePath(String fileName) {
        File file = new File(fileName);
        return file.getAbsolutePath();
    }

    /**
     * 将DOS/Windows格式的路径转换为UNIX/Linux格式的路径。 其实就是将路径中的"\"全部换为"/"，因为在某些情况下我们转换为这种方式比较方便，
     * 某中程度上说"/"比"\"更适合作为路径分隔符，而且DOS/Windows也将它当作路径分隔符。
     * 
     * @param filePath
     *            转换前的路径
     * @return 转换后的路径
     * @since 0.4
     */
    public static String toUNIXpath(String filePath) {
        return filePath.replace('\\', '/');
    }

    /**
     * 从文件名得到UNIX风格的文件绝对路径。
     * 
     * @param fileName
     *            文件名
     * @return 对应的UNIX风格的文件路径
     * @since 0.4
     * @see #toUNIXpath(String filePath) toUNIXpath
     */
    public static String getUNIXfilePath(String fileName) {
        File file = new File(fileName);
        return toUNIXpath(file.getAbsolutePath());
    }

    /**
     * 得到文件的类型。 实际上就是得到文件名中最后一个“.”后面的部分。
     * 
     * @param fileName
     *            文件名
     * @return 文件名中的类型部分
     * @since 0.5
     */
    public static String getTypePart(String fileName) {
        int point = fileName.lastIndexOf('.');
        int length = fileName.length();
        if (point == -1 || point == length - 1) {
            return "";
        }
        else {
            return fileName.substring(point + 1, length);
        }
    }

    /**
     * 得到文件的类型。 实际上就是得到文件名中最后一个“.”后面的部分。
     * 
     * @param file
     *            文件
     * @return 文件名中的类型部分
     * @since 0.5
     */
    public static String getFileType(File file) {
        return getTypePart(file.getName());
    }

    /**
     * 得到文件的名字部分。 实际上就是路径中的最后一个路径分隔符后的部分。
     * 
     * @param fileName
     *            文件名
     * @return 文件名中的名字部分
     * @since 0.5
     */
    public static String getNamePart(String fileName) {
        int point = getPathLastIndex(fileName);
        int length = fileName.length();
        if (point == -1) {
            return fileName;
        }
        else if (point == length - 1) {
            int secondPoint = getPathLastIndex(fileName, point - 1);
            if (secondPoint == -1) {
                if (length == 1) {
                    return fileName;
                }
                else {
                    return fileName.substring(0, point);
                }
            }
            else {
                return fileName.substring(secondPoint + 1, point);
            }
        }
        else {
            return fileName.substring(point + 1);
        }
    }

    /**
     * 得到文件名中的父路径部分。 对两种路径分隔符都有效。 不存在时返回""。 如果文件名是以路径分隔符结尾的则不考虑该分隔符，例如"/path/"返回""。
     * 
     * @param fileName
     *            文件名
     * @return 父路径，不存在或者已经是父目录时返回""
     * @since 0.5
     */
    public static String getPathPart(String fileName) {
        int point = getPathLastIndex(fileName);
        int length = fileName.length();
        if (point == -1) {
            return "";
        }
        else if (point == length - 1) {
            int secondPoint = getPathLastIndex(fileName, point - 1);
            if (secondPoint == -1) {
                return "";
            }
            else {
                return fileName.substring(0, secondPoint);
            }
        }
        else {
            return fileName.substring(0, point);
        }
    }

    /**
     * 得到路径分隔符在文件路径中首次出现的位置。 对于DOS或者UNIX风格的分隔符都可以。
     * 
     * @param fileName
     *            文件路径
     * @return 路径分隔符在路径中首次出现的位置，没有出现时返回-1。
     * @since 0.5
     */
    public static int getPathIndex(String fileName) {
        int point = fileName.indexOf('/');
        if (point == -1) {
            point = fileName.indexOf('\\');
        }
        return point;
    }

    /**
     * 得到路径分隔符在文件路径中指定位置后首次出现的位置。 对于DOS或者UNIX风格的分隔符都可以。
     * 
     * @param fileName
     *            文件路径
     * @param fromIndex
     *            开始查找的位置
     * @return 路径分隔符在路径中指定位置后首次出现的位置，没有出现时返回-1。
     * @since 0.5
     */
    public static int getPathIndex(String fileName, int fromIndex) {
        int point = fileName.indexOf('/', fromIndex);
        if (point == -1) {
            point = fileName.indexOf('\\', fromIndex);
        }
        return point;
    }

    /**
     * 得到路径分隔符在文件路径中最后出现的位置。 对于DOS或者UNIX风格的分隔符都可以。
     * 
     * @param fileName
     *            文件路径
     * @return 路径分隔符在路径中最后出现的位置，没有出现时返回-1。
     * @since 0.5
     */
    public static int getPathLastIndex(String fileName) {
        int point = fileName.lastIndexOf('/');
        if (point == -1) {
            point = fileName.lastIndexOf('\\');
        }
        return point;
    }

    /**
     * 得到路径分隔符在文件路径中指定位置前最后出现的位置。 对于DOS或者UNIX风格的分隔符都可以。
     * 
     * @param fileName
     *            文件路径
     * @param fromIndex
     *            开始查找的位置
     * @return 路径分隔符在路径中指定位置前最后出现的位置，没有出现时返回-1。
     * @since 0.5
     */
    public static int getPathLastIndex(String fileName, int fromIndex) {
        int point = fileName.lastIndexOf('/', fromIndex);
        if (point == -1) {
            point = fileName.lastIndexOf('\\', fromIndex);
        }
        return point;
    }

    /**
     * 将文件名中的类型部分去掉。
     * 
     * @param filename
     *            文件名
     * @return 去掉类型部分的结果
     * @since 0.5
     */
    public static String trimType(String filename) {
        int index = filename.lastIndexOf(".");
        if (index != -1) {
            return filename.substring(0, index);
        }
        else {
            return filename;
        }
    }

    /**
     * 得到相对路径。 文件名不是目录名的子节点时返回文件名。
     * 
     * @param pathName
     *            目录名
     * @param fileName
     *            文件名
     * @return 得到文件名相对于目录名的相对路径，目录下不存在该文件时返回文件名
     * @since 0.5
     */
    public static String getSubpath(String pathName, String fileName) {
        int index = fileName.indexOf(pathName);
        if (index != -1) {
            return fileName.substring(index + pathName.length() + 1);
        }
        else {
            return fileName;
        }
    }

    /**
     * 拷贝文件。
     * 
     * @param fromFileName
     *            源文件名
     * @param toFileName
     *            目标文件名
     * @return 成功生成文件时返回true，否则返回false
     * @since 0.6
     */
    public static boolean copy(String fromFileName, String toFileName) {
        return copy(fromFileName, toFileName, false);
    }

    /**
     * 拷贝文件。
     * 
     * @param fromFileName
     *            源文件名
     * @param toFileName
     *            目标文件名
     * @param override
     *            目标文件存在时是否覆盖
     * @return 成功生成文件时返回true，否则返回false
     * @since 0.6
     */
    public static boolean copy(String fromFileName, String toFileName, boolean override) {
        File fromFile = new File(fromFileName);
        File toFile = new File(toFileName);

        if (!fromFile.exists() || !fromFile.isFile() || !fromFile.canRead()) {
            return false;
        }

        if (toFile.isDirectory()) {
            toFile = new File(toFile, fromFile.getName());

        }
        if (toFile.exists()) {
            if (!toFile.canWrite() || override == false) {
                return false;
            }
        }
        else {
            String parent = toFile.getParent();
            if (parent == null) {
                parent = System.getProperty("user.dir");
            }
            File dir = new File(parent);
            if (!dir.exists() || dir.isFile() || !dir.canWrite()) {
                return false;
            }
        }

        FileInputStream from = null;
        FileOutputStream to = null;
        try {
            from = new FileInputStream(fromFile);
            to = new FileOutputStream(toFile);
            byte[] buffer = new byte[4096];
            int bytes_read;
            while ((bytes_read = from.read(buffer)) != -1) {
                to.write(buffer, 0, bytes_read);
            }
            return true;
        }
        catch (IOException e) {
            return false;
        }
        finally {
            if (from != null) {
                try {
                    from.close();
                }
                catch (IOException e) {
                    System.err.println("Exception when close source file");
                }
            }
            if (to != null) {
                try {
                    to.close();
                }
                catch (IOException e) {
                    System.err.println("Exception when close target file");
                }
            }
        }
    }

    /**
     * 查找给定路径下的某个文件
     * 
     * @param rootPath
     *            给定全路径
     * @param fileName
     *            查找文件名
     * @return
     * @author lyl
     */
    public static File findFile(String rootPath, String fileName) {
        System.out.println("rootPath=" + rootPath);
        File file = new File(rootPath);
        File ret = null;
        File[] files = listAll(file);
        System.out.println("files=" + files.length);
        for (int i = 0; i < files.length; i++) {
            System.out.println("files[i]=" + files[i].getPath());
            if (files[i].getName().equals(fileName)) {
                return files[i];
            }
            else if (files[i].isDirectory()) {
                File f = findFile(files[i].getAbsolutePath(), fileName);
                if (f != null)
                    return f;
            }
        }
        return ret;
    }

    /**
     * 将文件对象转成字节流
     * 
     * @param file
     * @return
     * @throws IOException
     */
    public static byte[] fileToStream(File file) throws IOException {
        BufferedInputStream input = new BufferedInputStream(new FileInputStream(file));
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        final int BUFSIZ = 1024;
        byte inbuf[] = new byte[BUFSIZ];
        int n;
        while ((n = input.read(inbuf, 0, BUFSIZ)) != -1)
            output.write(inbuf, 0, n);
        input.close();
        output.close();
        return output.toByteArray();
    }

    /**
     * streamToByteArray
     * 
     * @param stream
     * @return
     * @throws Exception
     */
    public static byte[] streamToByteArray(InputStream stream) throws Exception {
        try {
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            final int BUFSIZ = 1024;
            byte inbuf[] = new byte[BUFSIZ];
            int n;
            while ((n = stream.read(inbuf, 0, BUFSIZ)) != -1)
                output.write(inbuf, 0, n);
            return output.toByteArray();
        }
        catch (IllegalStateException e) {
            e.printStackTrace();
            throw new Exception(e);
        }
        catch (IOException e) {
            e.printStackTrace();
            throw new Exception(e);
        }
    }

    /**
     * 得到当前目录下的子目录名称
     * 
     * @param path
     * @return
     */
    public static String[] getSubDirNames(String path) {
        File file = new File(path);
        String[] tepfilelist = file.list();
        int j = 0;
        for (int i = 0; i < tepfilelist.length; i++) {
            File readfile = new File(path + "\\" + tepfilelist[i]);
            if (readfile.isDirectory()) {
                j++;
            }
        }
        String[] filelist = new String[j];
        j = 0;
        for (int i = 0; i < tepfilelist.length; i++) {
            File readfile = new File(path + "\\" + tepfilelist[i]);
            if (readfile.isDirectory()) {
                filelist[j] = tepfilelist[i];
                j++;
            }
        }
        return filelist;
    }

    /*
     * 保存文件 @param stream @return
     */
    public static void saveFile(BufferedReader reader, String outFileName) {
        try {
            BufferedWriter outfile = new BufferedWriter(new FileWriter(outFileName));
            // 循环读取reader文件
            while (true) {
                try {
                    outfile.write(reader.readLine());
                    outfile.newLine();
                }
                catch (Exception e) {
                    break;
                }
            }

            // 关闭文件
            outfile.close();
            reader.close();
        }
        catch (Exception e) {
            // TODO: handle exception
        }
    }

    /**
     * 保存字符串到文件中
     * 
     * @param fileName
     * @param str
     * @throws DcasException
     */
    public static String saveFile(String fileName, String str) {
        FileWriter fileWriter;
        try {
            File file = new File(fileName);
            if (file.exists()) {
                // System.out.println(fileName + "已存在！");
                file.delete();
            }
            if (!file.getParentFile().exists())
                file.getParentFile().mkdirs();
            fileWriter = new FileWriter(file);
            fileWriter.write(str);
            fileWriter.flush();
            return fileName;
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String saveFile(String fileName, byte[] bytes) {
        FileOutputStream output;
        try {
            if (bytes == null)
                return fileName;
            makeDirectory(fileName);
            output = new FileOutputStream(fileName);
            // 得到网络资源的字节数组,并写入文件
            output.write(bytes);
            output.close();
            return fileName;
        }
        catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    public static void copyDir(File srcDir, File dstDir) throws IOException {
        if (srcDir.isDirectory()) {
            if (!dstDir.exists()) {
                dstDir.mkdir();
            }

            String[] children = srcDir.list();
            for (int i = 0; i < children.length; i++) {
                copyDir(new File(srcDir, children[i]), new File(dstDir, children[i]));
            }
        }
        else {
            copy(srcDir.getAbsolutePath(), dstDir.getAbsolutePath());
        }
    }

    public static HashMap <File,byte[]> dirToBytes(String dirName) {
        HashMap <File,byte[]> map = new HashMap <File,byte[]>();
        File[] files = listAll(dirName);
        for (int i = 0; i < files.length; i++) {
            if (files[i].isDirectory())
                map.put(files[i], null);
            else
                try {
                    map.put(files[i], fileToStream(files[i]));
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
        }
        return map;
    }

    public static void copyDir(HashMap <File,byte[]> srcDirData, String srcDirName,
            String destDirName) {
        // String dirName = "d:\\gov.cn.200801091848390491806";
        // String destDir = "d:\\test\\gov.cn.200801091848390491806";
        File dest = new File(destDirName);
        dest.mkdir();
        HashMap <File,byte[]> map = dirToBytes(srcDirName);
        Set <File> keys = map.keySet();
        Iterator <File> iter = keys.iterator();
        while (iter.hasNext()) {
            File key = iter.next();
            if (map.get(key) == null) {
                File dir = new File(key.getPath().replace(srcDirName, destDirName));
                dir.mkdir();
            }
            else {
                String fileName = key.getPath().replace(srcDirName, destDirName);
                FileUtil.saveFile(fileName, map.get(key));
            }
        }
    }

    public static boolean forceDelete(File f) {
        boolean result = false;
        int tryCount = 0;
        while (!result && tryCount++ < 10) {
            System.out.println("try to delete file " + f.getName() + " times:" + tryCount);
            System.gc();
            result = f.delete();
        }
        return result;
    }

    public static void main(String[] args) {
        // System.out.println(getSubDirNames("D:\\dcas").length);
        // System.out.println(makeDirectory("d:/site/original/gov.cn.200712261653277716777/jrzg/2007-12/25/content_843585.htm"));
        /*
         * try { FileUtil .copyDir( new File("D:/eclipse3.3/original/gov-tpyw.200712270022422276798"), new File(
         * "D:/apache-tomcat-5.5.25/webapps/dcas-console-1.0/gov-tpyw.200712270022422276798")); } catch (IOException e) { //
         * TODO Auto-generated catch block e.printStackTrace(); }
         */
        FileUtil
                .deleteFile("D:\\apache-tomcat-5.5.25\\webapps\\dcas-console-1.0\\gov.cn.200801091849062084294.html");
    }
}